home *** CD-ROM | disk | FTP | other *** search
- /* (C) June 1994 Stuart Cheshire <cheshire@cs.stanford.edu>
-
- In a stunning piece of developer support, when Apple added the zoom box
- to the Window Manager, the user interface police mandated how it should
- work in a user-friendly way, and then provided a system call that didn't
- do it! Considering that at the time many developers (especially shareware
- authors) didn'it have access to large screens or multiple-monitor setups,
- expecting every developer to engineer their own user-friendly multiple-
- monitor-aware solution was ridiculous.
-
- Having just been through this grief for Bolo, I'm making the solution
- available for other developers.
-
- SmartZoomWindow is written to be a simple drop-in replacement for Apple's
- ZoomWindow call so you shouldn't have to make much change to your program.
- The first three parameters are the same, and the fourth is a pointer to a
- Rectangle giving the maximum and minimum sensible dimensions that the window
- should be zoomed to.
-
- ZoomWindow(WindowPtr theWindow, short partCode, Boolean front);
- SmartZoomWindow(WindowPtr win, short partCode, Boolean front, const Rect *Limits);
-
- The Rect is defined in a way compatible to GrowWindow -- ie top,left give the
- minimum height and width of the window, and bottom, right give the maximum
- height and width of the window *plus one*. Note that if you want the maximum
- window size to be 100x100, this means that you have to set the limits to 101.
- I know that this is bizarre, but it's the way that GrowWindow works, even
- though that's not what Inside Macintosh says. (As an aside, I wonder how
- many programs are bitten by this? I know the Finder has this bug -- open the
- "About This Macintosh" window and try resizing it with the zoom box and with
- the grow box. If you look closely you will see that when you repeatedly click
- on the zoom box, the width oscillates in and out by one pixel.)
-
- If you don't have any special constraints on what size the window should
- be, you can set the minimums to zero and the maximums to 0x7FFF, and
- SmartZoomWindow will just take care of zooming the window up to the full
- size of the monitor that it is on, instead of zooming it back to the main
- (menu bar) screen like the normal ZoomWindow call does.
-
- After calling WaitNextEvent and FindWindow as usual, your code should proceed
- like this:
-
- if (TrackBox(win, theEventRecord->where, partcode))
- {
- Rect Limits = { 0, 0, 0x7FFF, 0x7FFF }; // Or whatever limits are appropriate
- SetPort(win);
- EraseRect(&win->portRect);
- SmartZoomWindow(win, partcode, TRUE, &Limits);
- }
-
- SmartZoomWindow is slightly smarter than the algorithm that the Finder uses,
- which makes the windows fly up to the top left corner of the monitor if
- resizing them in the current position would result in part of the window going
- off the screen. SmartZoomWindow will just move the window the minimum amount
- required to keep it on the screen.
-
- Some of the inspiration for this code came from DoBetterWZoom.c on the Apple
- Developer CD. I would credit the author, but there is no name on that code.
- */
-
- #include <StuTypes.h>
-
- local GDHandle findDominantDevice(Rect *theRect)
- {
- long sectArea, greatestArea = 0;
- GDHandle dominantGDevice = NULL, dev = GetDeviceList();
- while (dev) // for every device
- {
- if (TestDeviceAttribute(dev, screenDevice) && // that's an active screen
- TestDeviceAttribute(dev, screenActive))
- {
- Rect r;
- SectRect(theRect, &(**dev).gdRect, &r); // find out the intersection area
- sectArea = (long)(r.right - r.left) * (long)(r.bottom - r.top);
- if (greatestArea < sectArea) { greatestArea = sectArea; dominantGDevice = dev; }
- }
- dev = GetNextDevice(dev);
- }
- return(dominantGDevice);
- }
-
- // The window will be zoomed to fill the screen it is already on,
- // subject to the size constraints of the Limits rectangle.
- // win = window to be zoomed
- // partCode = inZoomIn or inZoomOut
- // front = Bring window to front after zooming? (Normally TRUE)
- // Limits.top = minimum allowable window height
- // Limits.left = minimum allowable window width
- // Limits.bottom = maximum sensible window height + 1
- // Limits.right = maximum sensible window width + 1
- // Note: The maximums are actually one greater than the actual maximum allowed sizes,
- // for consistency with GrowWindow
- local void SmartZoomWindow(WindowPtr win, short partCode, Boolean front, const Rect *Limits)
- {
- // Zooming in is easy. Only zooming out needs extra smarts
- if (partCode == inZoomOut)
- {
- SysEnvRec sysenvirons;
- short hsize = Limits->right - 1;
- short vsize = Limits->bottom - 1;
- WindowPeek wp = (WindowPeek)win;
- Rect *zoomRect = &(*(WStateDataHandle)wp->dataHandle)->stdState;
- Rect *globalFrameRect = &(*wp->strucRgn)->rgnBBox;
- Rect globalPortRect = win->portRect;
- Rect target = qd.screenBits.bounds; // Start with a reasonable default
- target.top += GetMBarHeight();
- LocalToGlobal(&topLeft(globalPortRect)); // calculate the window's portRect
- LocalToGlobal(&botRight(globalPortRect)); // in global coordinates
-
- // *** TASK 1
- // Pick a monitor (if necessary) and work out the maximum allowable rectangle on it
- SysEnvirons(1,&sysenvirons);
- if (sysenvirons.hasColorQD)
- {
- GDHandle dev = findDominantDevice(globalFrameRect);
- if (dev)
- {
- target = (*dev)->gdRect;
- if (dev == GetMainDevice()) target.top += GetMBarHeight();
- }
- }
-
- // *** TASK 2
- // Reduce the logical target rectangle a little to allow for the frame
- // around the window, and a couple of pixels extra for aesthetic reasons
- target.top += (globalPortRect.top - globalFrameRect->top ) + 2;
- target.left += (globalPortRect.left - globalFrameRect->left ) + 2;
- target.bottom += (globalPortRect.bottom - globalFrameRect->bottom) - 1;
- target.right += (globalPortRect.right - globalFrameRect->right ) - 1;
-
- // *** TASK 3
- // Make sure the target rectangle is big enough for the minimum size
- if (target.bottom < target.top + Limits->top ) target.bottom = target.top + Limits->top;
- if (target.right < target.left + Limits->left) target.right = target.left + Limits->left;
-
- // *** TASK 4
- // Make sure the desired (maximum) size fits within the target rectangle
- if (vsize > target.bottom - target.top) vsize = target.bottom - target.top;
- if (hsize > target.right - target.left) hsize = target.right - target.left;
-
- // *** TASK 5
- // Start off with a zoom rectangle obtained by simply resizing the window
- // in the current position
- zoomRect->top = globalPortRect.top;
- zoomRect->left = globalPortRect.left;
- zoomRect->bottom = globalPortRect.top + vsize;
- zoomRect->right = globalPortRect.left + hsize;
-
- // *** TASK 6
- // If Window is falls outside the limits, shift it the minimum amount to bring it inside
- if (zoomRect->top < target.top ) OffsetRect(zoomRect, 0, target.top - zoomRect->top);
- if (zoomRect->left < target.left ) OffsetRect(zoomRect, target.left - zoomRect->left, 0);
- if (zoomRect->bottom > target.bottom) OffsetRect(zoomRect, 0, target.bottom - zoomRect->bottom);
- if (zoomRect->right > target.right ) OffsetRect(zoomRect, target.right - zoomRect->right, 0);
- }
- ZoomWindow(win, partCode, front);
- }
-
-